package org.ytor.core.sqlflow.dsl.token;

import java.util.ArrayList;
import java.util.List;

/**
 * created by yangtong on 2025/8/6 19:21:16
 * <br/>
 * TOKEN词法分析器
 */
public class Tokenizer {

    private final String input;
    private int pos = 0;
    private int length;

    public Tokenizer(String input) {
        this.input = input;
        this.length = input.length();
    }

    public List<Token> tokenize() {
        List<Token> tokens = new ArrayList<>();

        while (pos < length) {
            char c = peek();

            if (Character.isWhitespace(c)) {
                advance(); // 跳过空格
            } else if (c == '(') {
                tokens.add(new Token(TokenType.LPAREN, "("));
                advance();
            } else if (c == ')') {
                tokens.add(new Token(TokenType.RPAREN, ")"));
                advance();
            } else if (c == ',') {
                tokens.add(new Token(TokenType.COMMA, ","));
                advance();
            } else if (c == '\'' || c == '"') {
                tokens.add(readString());
            } else if (Character.isDigit(c)) {
                tokens.add(readNumber());
            } else if (isIdentifierStart(c)) {
                tokens.add(readWord(tokens)); // 传入tokens用于处理复合操作符
            } else if (isOperatorChar(c)) {
                tokens.add(readOperator());
            } else {
                throw new RuntimeException("Unexpected char: " + c);
            }
        }

        tokens.add(new Token(TokenType.EOF, ""));
        return tokens;
    }

    private char peek() {
        if (pos >= length) return '\0';
        return input.charAt(pos);
    }

    private char peekNext() {
        if (pos + 1 >= length) return '\0';
        return input.charAt(pos + 1);
    }

    private void advance() {
        pos++;
    }

    /**
     * 读取完整的字符串，并返回字符串token
     */
    private Token readString() {
        char quote = peek();
        advance(); // skip opening quote
        StringBuilder sb = new StringBuilder();

        while (pos < length && peek() != quote) {
            sb.append(peek());
            advance();
        }

        if (pos == length) {
            throw new RuntimeException("Unclosed string literal");
        }

        advance(); // skip closing quote
        return new Token(TokenType.STRING, sb.toString());
    }

    /**
     * 读取完整的数值，并返回数值token
     */
    private Token readNumber() {
        StringBuilder sb = new StringBuilder();
        while (pos < length && (Character.isDigit(peek()) || peek() == '.')) {
            sb.append(peek());
            advance();
        }
        return new Token(TokenType.NUMBER, sb.toString());
    }

    /**
     * 处理所有以字母开头的变量、标识符或者关键字，并返回token
     * 需要处理复合操作符如 "is not"， "not is", "not in", "not like" 等
     */
    private Token readWord(List<Token> tokens) {
        StringBuilder sb = new StringBuilder();
        while (pos < length && (Character.isLetterOrDigit(peek()) || peek() == '-' || peek() == '.' || peek() == '_')) {
            sb.append(peek());
            advance();
        }
        String word = sb.toString();

        // 处理复合操作符
        Token result = handleCompoundOperators(word, tokens);
        if (result != null) {
            return result;
        }

        // 判断单个关键字和逻辑操作符
        return switch (word) {
            case "and" -> new Token(TokenType.AND, word);
            case "or" -> new Token(TokenType.OR, word);
            case "null", "empty", "true", "false" -> new Token(TokenType.KEYWORD, word);
            case "is" -> new Token(TokenType.IS, word);
            case "in" -> new Token(TokenType.IN, word);
            case "like" -> new Token(TokenType.LIKE, word);
            case "between" -> new Token(TokenType.BETWEEN, word);
            default -> new Token(TokenType.IDENT, word);
        };
    }

    /**
     * 处理复合操作符
     * 需要向前看下一个单词，如果组成复合操作符则处理，否则返回null
     */
    private Token handleCompoundOperators(String currentWord, List<Token> tokens) {
        switch (currentWord) {
            case "is" -> {
                // 检查 "is not"
                String nextWord = peekNextWord();
                if ("not".equals(nextWord)) {
                    consumeNextWord(); // 消费 "not"
                    return new Token(TokenType.IS_NOT, "is not");
                }
                return null; // 让调用方处理单个 "is"
            }
            case "not" -> {
                // 检查 "not in", "not like", "not between"
                String nextWord = peekNextWord();
                return switch (nextWord) {
                    case "is" -> {
                        consumeNextWord();
                        yield new Token(TokenType.NOT_IS, "not is");
                    }
                    case "in" -> {
                        consumeNextWord();
                        yield new Token(TokenType.NOT_IN, "not in");
                    }
                    case "like" -> {
                        consumeNextWord();
                        yield new Token(TokenType.NOT_LIKE, "not like");
                    }
                    case "between" -> {
                        consumeNextWord();
                        yield new Token(TokenType.NOT_BETWEEN, "not between");
                    }
                    case null -> throw new RuntimeException("错误的语法：【" + input.substring(0, pos) + "】");
                    default -> null; // 让调用方处理单个 "not"
                };
            }
        }
        return null;
    }

    /**
     * 向前看下一个单词，不移动pos
     */
    private String peekNextWord() {
        int savedPos = pos;

        // 跳过空格
        while (pos < length && Character.isWhitespace(peek())) {
            advance();
        }

        if (pos >= length || !isIdentifierStart(peek())) {
            pos = savedPos;
            return null;
        }

        // 读取单词
        StringBuilder sb = new StringBuilder();
        while (pos < length && (Character.isLetterOrDigit(peek()) || peek() == '-' || peek() == '.' || peek() == '_')) {
            sb.append(peek());
            advance();
        }

        String word = sb.toString().toLowerCase();
        pos = savedPos; // 恢复位置
        return word;
    }

    /**
     * 消费下一个单词（已经通过peekNextWord确认存在）
     */
    private void consumeNextWord() {
        // 跳过空格
        while (pos < length && Character.isWhitespace(peek())) {
            advance();
        }

        // 跳过单词
        while (pos < length && (Character.isLetterOrDigit(peek()) || peek() == '-' || peek() == '.' || peek() == '_')) {
            advance();
        }
    }

    /**
     * 处理 ==, !=, >=, <=，并返回token
     */
    private Token readOperator() {
        char first = peek();
        advance();

        if (pos < length) {
            char second = peek();
            String op = "" + first + second;
            switch (op) {
                case "==" -> {
                    advance();
                    return new Token(TokenType.EQ, op);
                }
                case "!=" -> {
                    advance();
                    return new Token(TokenType.NE, op);
                }
                case ">=" -> {
                    advance();
                    return new Token(TokenType.GE, op);
                }
                case "<=" -> {
                    advance();
                    return new Token(TokenType.LE, op);
                }
            }
        }

        // 单字符操作符
        return switch (first) {
            case '>' -> new Token(TokenType.GT, String.valueOf(first));
            case '<' -> new Token(TokenType.LT, String.valueOf(first));
            default -> throw new RuntimeException("Unknown operator: " + first);
        };
    }

    /**
     * 判断某个字符是否可以作为"变量名、函数名、关键字等"的开头
     */
    private boolean isIdentifierStart(char c) {
        return Character.isLetter(c) || c == '_';
    }

    /**
     * 判断某个字符是否可能是"符号类操作符"的开头
     */
    private boolean isOperatorChar(char c) {
        return "=!<>".indexOf(c) >= 0;
    }
}